<?php

namespace app\miniapp\controller\v3;


use app\common\cachemodel\AppConfig;
use app\common\cachemodel\MiniAppUser;
use app\common\cachemodel\Recode;
use app\common\cachemodel\Wechat;
use app\common\lib\Alidayu;
use app\common\lib\exception\ApiException;
use app\common\lib\IAuth;
use app\common\lib\Util;
use app\common\model\AppTelLogin;
use app\miniapp\controller\v1\Auth;
use think\Cache;
use think\Db;
use think\Log;
use XznsTools\cache\CacheManage;

class User extends Auth
{
    private const SWAN_SESSION_KEY = 'miniapp:session_key:swan';
    private const WEAPP_SESSION_KEY = 'miniapp:session_key:weapp';
    private const QQ_SESSION_KEY = 'miniapp:session_key:qq';
    private const TT_SESSION_KEY = 'miniapp:session_key:tt';

    public function decodeBindPhone()
    {
        $encryptedData = input('encrypted_data/s', '');
        $iv = input('iv/s', '');
        $code = input('code/s', '');

        if (!in_array($this->headers['app-type'], ['ALIPAY', 'TT', 'SWAN', 'QQ', 'WEAPP'], true))
        {   // TODO 支付宝小程序 缺失
            return show(config('code.error'), '请求异常');
        }
        $CacheSessionKey = $this->headers['app-type'] . '_SESSION_KEY';

        $sessionKey = Cache::get(constant('self::' . $CacheSessionKey) . ':' . $this->miniInfo['id']);

        $mini_item_conf = config('miniapp')[$this->headers['app-type']];
        if (!$sessionKey)
        {
            $sessionKey = $this->getSessionKey($code, $this->headers['app-type'], $mini_item_conf);
            if (empty($sessionKey))
            {
                return show(config('code.error'), '服务异常');
            }
        }
        $tel = '';
        switch ($this->headers['app-type'])
        {
            case 'WEAPP':
            case 'QQ':
                // QQ或者微信
                $res = $this->wechatOrQQDecrypt($mini_item_conf['app_id'], $sessionKey, $encryptedData, $iv);
                $tel = $res['phoneNumber'] ?? '';
                break;
            case 'TT':
                // 字节跳动
                $res = $this->ttDecrypt($encryptedData, $iv, $sessionKey);
                $tel = $res['phoneNumber'] ?? '';
                break;
            case 'SWAN':
                // 百度小程序
                $res = $this->swanDecrypt($encryptedData, $iv, $mini_item_conf['app_key'], $sessionKey);
                $tel = '';
                break;
        }

        if (!$tel)
        {
            return show(423, '服务异常,绑定失败!');
        }

        return $this->userBindPhone($tel);
    }

    /**
     * 绑定手机号
     * @throws \Exception
     */
    public function bindPhone()
    {
        $tel = input('tel/s', '');
        $code = input('code/s', '');

        $areaCode = input('area_code/d', 86);
        if (!$tel)
        {
            return show(config('code.error'), '请求异常，绑定失败');
        }

        //检查固定验证码登录
        $appConfig = config('app');
        if (!in_array($tel, $appConfig['mobile'])
            || ($areaCode === 86 && $code != substr($appConfig['code'] * (substr($tel, 3, 6)), 0, 6)))
        {
            $codeCache = Cache::get('miniapp.' . $this->headers['app-type'] . '.' . $tel);

            if (!$codeCache || !$code || $code !== $codeCache)
            {
                return show(config('code.error'), '验证码错误，绑定失败');
            }
        }

        $tel = $areaCode === 86 ? $tel : $areaCode . '-' . $tel;

        return $this->userBindPhone($tel);
    }

    /**
     * 绑定手机号
     * @param string $tel
     * @param string $rid
     * @return \think\response\Json
     * @throws \Exception
     */
    private function userBindPhone(string $tel = '')
    {
        $envType = $this->headers['app-type'];
        $time = $this->request->time();
        if (!$tel)
        {
            return show(config('code.error'), '绑定失败,请稍后重试');
        }

        if ($this->user && $this->user['tel'])
        {
            return show(421, '该账户已绑定手机号');
        }

        if ($envType === 'WEB')
        {   // H5 绑定手机号 获取未注销的账户
            $miniInfo = (new MiniAppUser())->where('status', '<>', 1)->where('tel', $tel)->order('id desc')->field('id,uid,status')->find();
            if ($miniInfo)
            {
                if ($miniInfo['status'] == 0)
                {   // 登陆
                    $tokenData = $this->getUserTokenInfo($tel);
                    // 更新数据库缓存信息
                    (new MiniAppUser())->save($tokenData, ['id' => $miniInfo['id']]);

                    $recodeId = $this->getSimulationRid((int)$miniInfo['uid']); // 获取用户登陆rid 或者 模拟用户rid

                    if ($recodeId)
                    {
                        $recordInfo = (new \app\common\cachemodel\Recode())->getRecodeLists($miniInfo['uid'], 1);
                    }

                    return show(config('code.success'), '绑定成功', [
                        'rid' => $recodeId,
                        'access_token' => $this->encryptWithOpenssl($tokenData['access_token'] . '||' . $miniInfo['id']),
                        'recode_info' => $recordInfo ?? null,
                        'mid' => $miniInfo['id']
                    ]);

                } elseif ($miniInfo['status'] == 2)
                {
                    throw new ApiException('该账号禁止登录', 200, 0);
                }
            }
            // 查找数据库 判断该手机号是否已经被注册
            $userInfo = (new \app\common\cachemodel\User())->where(['tel' => $tel])->field('id,forbidden,has_self_recode')->find();

            if ($userInfo && $userInfo['forbidden'] == 1)
            {
                return show(config('code.error'), '该手机号被禁用，绑定失败');
            }
            $resRecodeInfo = null;
            $rid = 0;
            if ($userInfo)
            {   // 注册过
                $uid = $userInfo['id'];
                if ($userInfo['has_self_recode'])
                {   // 有自己的档案 获取自己的档案ID
                    $recordInfo = (new \app\common\cachemodel\Recode())->getRecodeLists($uid, 1);
                    if ($recordInfo)
                    {
                        $rid = (int)($recordInfo['id'] ?? 0);
                        $resRecodeInfo = $recordInfo;
                    }
                }
            } else
            {   // 没有注册过  用户表注册
                $nickname = $recodeInfo['name'] ?? '';
                $sex = $recodeInfo['type'] ?? 0;
                // 用户表账户注册
                $ip = $this->request->ip();
                // 第一次登录 注册数据
                $user['uen'] = substr(md5($this->request->time(true) . mt_rand(0, 1000000) . uniqid()), 0, 12);
                $user['nickname'] = $nickname;
                $user['reg_time'] = $time;
                $user['reg_ip'] = $ip;
                $user['last_login'] = $time;
                $user['last_ip'] = $ip;
                $user['last_active'] = $time;
                $user['status'] = 1;
                $user['sex'] = $sex;
                $user['tel'] = $tel;
                $user['is_first_login'] = 1;
                $user['uc_id'] = md5($tel . $this->request->time(true));
                $user['has_self_recode'] = 0;
                $user['channel'] = Util::getChannelId();
                try
                {
                    $uid = (new \app\common\cachemodel\User())->insertGetId($user);
                }
                catch (\Exception $e)
                {
                    return show(config('code.error'), '注册失败,请稍后重试');
                }
            }

            $tokenData = $this->getUserTokenInfo($uid);
            // 小程序表注册
            $miniInfo = [
                'tel' => $tel,
                'mini_type' => $envType,
                'nickname' => $nickname ?? '',
                'avatar' => '',
                'sex' => $sex ?? '',
                'unionid' => '',
                'openid' => '',
                'city' => '',
                'province' => '',
                'country' => '',
                'access_token' => $tokenData['access_token'],
                'expires_in' => $tokenData['expires_in'],
                'create_time' => $time,
                'app_type' => 1,
                'status' => 0,
                'uid' => $uid,
                'channel' => Util::getChannelId() // 渠道ID
            ];

            $id = (new MiniAppUser())->SavaData($miniInfo);


            $accessToken = $this->encryptWithOpenssl($tokenData['access_token'] . '||' . $id);

            return show(config('code.success'), '绑定成功', [
                'rid' => $rid,
                'access_token' => $accessToken,
                'recode_info' => $resRecodeInfo,
                'mid' => $id
            ]);
        } else
        {   // 小程序绑定 手机号
            if (!$this->miniInfo)
            {
                return show(config('code.denied'), '登陆失效');
            }

            if (!in_array($this->headers['app-type'], ['WEAPP', 'TT'], true) && $this->miniInfo['uid'] > 0)
            {   // 关联uid 的账户说明不需要再绑定手机号了
                return show(421, '服务异常，绑定失败', ['rid' => (int)($this->user['rid'])]);
            }

            // 查找数据库 判断该手机号是否已经被注册
            $telInfo = (new \app\common\cachemodel\User())->where(['tel' => $tel])->field('id,forbidden,has_self_recode')->find();

            if ($telInfo && $telInfo['forbidden'] == 1)
            {
                return show(config('code.error'), '该手机号被禁用，绑定失败');
            }

            if ($telInfo)
            {
                if ($envType === 'WEAPP')
                {
                    // 判断手机号是否已经绑定了微信
                    $userWeInfo = (new Wechat())->alias('w')
                        ->where('w.uid', $telInfo['id'])
                        ->join('wechat_backup_07_18_14_43_00.app_user u', 'w.uid=u.id')
                        ->field('w.id,u.id uid,w.unionid,u.tel,u.has_self_recode')->find();

                    if ($userWeInfo)
                    {
                        if ($userWeInfo['unionid'] !== $this->miniInfo['unionid'])
                        {
                            return show(config('code.error'), '该手机号已绑定其他微信账户！');
                        }

                        if (!$this->miniInfo['uid'])
                        { // 如果账户未进行同步过微信账户  -> 同步
                            if ($this->checkPhoneHasBindAcount((int)$telInfo['id'], $envType, 1))
                            { // 判断 手机号是否已经绑定了小程序账户
                                return show(config('code.error'), '该手机号已绑定其他账户！');
                            }
                            (new MiniAppUser())->save(['uid' => $telInfo['id']], ['id' => $this->miniInfo['id']]);
                        }

                        if ($userWeInfo['has_self_recode'])
                        {   // 存在档案
                            $recordInfo = (new \app\common\cachemodel\Recode())->getRecodeLists($userWeInfo['uid'], 1);
                            if ($recordInfo)
                            {
                                $rid = (int)($recordInfo['id'] ?? 0);
                            }
                        } else
                        {
                            $rid = 0;
                        }

                        return show(config('code.success'), '绑定成功', [
                            'rid' => $rid,
                            'access_token' => $this->headers['access-token'],
                            'mid' => $this->miniInfo['id']
                        ]);
                    }
                } else
                {
                    if (!$this->miniInfo['uid'])
                    { // 如果账户绑定
                        if ($this->checkPhoneHasBindAcount((int)$telInfo['id'], $envType, 1))
                        { // 判断 手机号是否已经绑定了小程序账户
                            return show(config('code.error'), '该手机号已绑定其他账户！');
                        }
                        (new MiniAppUser())->save(['uid' => $telInfo['id']], ['id' => $this->miniInfo['id']]);

                        if ($telInfo['has_self_recode'])
                        {   // 存在档案
                            $recordInfo = (new \app\common\cachemodel\Recode())->getRecodeLists($telInfo['id'], 1);
                            if ($recordInfo)
                            {
                                $rid = (int)($recordInfo['id'] ?? 0);
                            }
                        } else
                        {
                            $rid = 0;
                        }

                        return show(config('code.success'), '绑定成功', [
                            'rid' => $rid,
                            'access_token' => $this->headers['access-token'],
                            'mid' => $this->miniInfo['id']
                        ]);
                    } else
                    {
                        return show(421, '已绑定手机号，绑定失败', ['rid' => (int)($this->user['rid'])]);
                    }
                }
            }

            if ($this->miniInfo['uid'])
            {   // 关联过账户
                $userInfo = (new Wechat())->alias('w')
                    ->where('w.uid', $this->miniInfo['uid'])
                    ->join('wechat_backup_07_18_14_43_00.app_user u', 'w.uid=u.id')
                    ->field('w.id,u.id uid,u.tel,u.has_self_recode')->find();

                if ($userInfo['tel'])
                {   // 如果已经绑定手机号 或者 已经有档案了 则绑定失败
                    return show(421, '已绑定手机号，绑定失败', ['rid' => (int)($this->user['rid'])]);
                }
                if ($userInfo['has_self_recode'] && $telInfo)
                {   // 如果手机号为空 档案存在 并且手机号页注册过了
                    return show(421, '该手机号已被注册，绑定失败', ['rid' => (int)($this->user['rid'])]);
                }

                if ($telInfo)
                {   // 手机号注册过 且 微信账户没档案 且 没有绑定过手机号
                    (new Wechat())->save(['uid' => $telInfo['id']], ['id' => $userInfo['id']]);
                    (new MiniAppUser())->save(['uid' => $telInfo['id']], ['id' => $this->miniInfo['id']]);
                    (new \app\common\model\User())->removeUserCache($telInfo['id']);
                    (new \app\common\model\User())->removeUserCache($userInfo['uid']);
                    (new \app\common\model\User())->save(['token' => ''], ['id' => $userInfo['uid']]);
                    $rid = 0;
                    if ($telInfo['has_self_recode'])
                    {   // 存在档案
                        $recordInfo = (new \app\common\cachemodel\Recode())->getRecodeLists($telInfo['id'], 1);
                        if ($recordInfo)
                        {
                            $rid = (int)($recordInfo['id'] ?? 0);
                        }
                    }

                    return show(config('code.success'), '绑定成功', [
                        'rid' => $rid,
                        'access_token' => $this->headers['access-token'],
                        'mid' => $this->miniInfo['id']
                    ]);
                } else
                {   // 手机号未注册过 且 没有绑定过手机号
                    (new \app\common\cachemodel\User())->save(['tel' => $tel], ['id' => $this->miniInfo['uid']]);
                    (new \app\common\model\User())->removeUserCache($this->miniInfo['uid']);
                    $rid = 0;
                    if ($userInfo['has_self_recode'])
                    {   // 存在档案
                        $recordInfo = (new \app\common\cachemodel\Recode())->getRecodeLists($this->miniInfo['uid'], 1);
                        if ($recordInfo)
                        {
                            $rid = (int)($recordInfo['id'] ?? 0);
                        }
                    }

                    return show(config('code.success'), '绑定成功', [
                        'rid' => $rid,
                        'access_token' => $this->headers['access-token'],
                        'mid' => $this->miniInfo['id']
                    ]);
                }
            }
            // 没关联旧账户
            if ($telInfo)
            {   // 手机注册过账户
                (new MiniAppUser())->save(['uid' => $telInfo['id']], ['id' => $this->miniInfo['id']]);

                $rid = 0;
                if ($telInfo['has_self_recode'])
                {   // 存在档案
                    $recordInfo = (new \app\common\cachemodel\Recode())->getRecodeLists($telInfo['id'], 1);
                    if ($recordInfo)
                    {
                        $rid = (int)($recordInfo['id'] ?? 0);
                    }
                }
                $uid = $telInfo['id'];
            } else
            {
                $ip = $this->request->ip();
                // 第一次登录 注册数据
                $user['uen'] = substr(md5($this->request->time(true) . mt_rand(0, 1000000) . uniqid()), 0, 12);
                $user['nickname'] = $this->miniInfo['nickname'];
                $user['reg_time'] = $time;
                $user['reg_ip'] = $ip;
                $user['last_login'] = $time;
                $user['last_ip'] = $ip;
                $user['last_active'] = $time;
                $user['status'] = 1;
                $user['sex'] = 1;
                $user['tel'] = $tel;
                $user['is_first_login'] = 1;
                $user['uc_id'] = md5($tel . $this->request->time(true));
                $user['has_self_recode'] = 0;
                $user['channel'] = Util::getChannelId();
                try
                {
                    $uid = (new \app\common\cachemodel\User())->insertGetId($user);
                    $user['id'] = $uid;
                }
                catch (\Exception $e)
                {
                    return show(config('code.error'), '绑定失败,请稍后重试');
                }

                (new MiniAppUser())->save(['uid' => $uid], ['id' => $this->miniInfo['id']]);
            }

            if ($envType === 'WEAPP')
            {   // 微信小程序绑定手机号 微信账户未注册->注册微信登陆账户
                $wechat_insert = [
                    'uid' => $uid,
                    'nickname' => $this->miniInfo['nickname'],
                    'sex' => $this->miniInfo['sex'],
                    'city' => $this->miniInfo['city'],
                    'province' => $this->miniInfo['province'],
                    'country' => $this->miniInfo['country'],
                    'headimgurl' => $this->miniInfo['avatar'],
                    'openid' => '',
                    'unionid' => $this->miniInfo['unionid'],
                    'create_time' => $time,
                    'update_time' => $time,
                ];

                (new Wechat())->SaveData($wechat_insert);
            }

            return show(config('code.success'), '绑定成功', [
                'rid' => $rid,
                'access_token' => $this->headers['access-token'],
                'mid' => $this->miniInfo['id']
            ]);
        }
    }

    /**
     * 档案添加
     */
    public function recodeAddOrEdit()
    {
        $name = input('name/s', '', 'htmlspecialchars');// 档案名称
        $type = input('type/d');// 档案类型  1男 | 2 女 | 3 男同 | 4 女同 | 5 事件 | 6 卜卦
        $year = input('year/d');// 出生时间-年
        $month = input('month/d');// 出生时间-月
        $day = input('day/d');// 出生时间-日
        $hour = input('hour/d');// 出生时间-时
        $minute = input('minute/d');// 出生时间-分
        $birth_country = input('birth_country/s', '', 'htmlspecialchars');// 出生地点-省
        $birth_province = input('birth_province/s', '', 'htmlspecialchars');// 出生地点-市
        $birth_city = input('birth_city/s', '', 'htmlspecialchars');// 出生地点-区
        $live_country = input('live_country/s', '', 'htmlspecialchars');// 现居地-省
        $live_province = input('live_province/s', '', 'htmlspecialchars');// 现居地-省
        $live_city = input('live_city/s', '', 'htmlspecialchars');// 现居地-省
        $long_deg = input('long_deg/d');// 经度-度
        $long_min = input('long_min/d');// 经度-分
        $ew = input('ew/d');// 东经|西经 1|-1
        $lat_deg = input('lat_deg/d');// 纬度-度
        $lat_min = input('lat_min/d');// 纬度-分
        $ns = input('ns/d');// 南纬北纬  1|-1
        $rid = input('rid', '');
        $resMsg = $rid ? '更新' : '创建';
        $avatar = '';
        $envType = $this->headers['app-type'];
        if (!$this->miniInfo)
        {   // 如果不说H5 且 没有 access_token 则返回登陆失效
            return show(config('code.denied'), '登陆失效');
        }

        $is_summer = 0;
        // 只判断大陆地区的夏令时
        if (!in_array($birth_country, ['香港特别行政区', '澳门特别行政区', '台湾省', '北美洲', '大洋洲', '非洲', '南美洲', '欧洲', '亚洲', '']))
        {
            $is_summer = isSummerTime($hour, $minute, 0, $month, $day, $year) ? 1 : 0;// 是否开启夏令时
        }

        $data = [
            'uid' => (int)$this->user['id'],
            'name' => $name,
            'type' => $type,
            'year' => $year,
            'month' => $month,
            'day' => $day,
            'hour' => $hour,
            'minute' => $minute,
            'birth_country' => $birth_country,
            'birth_province' => $birth_province,
            'birth_city' => $birth_city,
            'live_country' => $live_country,
            'live_province' => $live_province,
            'live_city' => $live_city,
            'long_deg' => $long_deg,
            'long_min' => $long_min,
            'ew' => $ew,
            'lat_deg' => $lat_deg,
            'lat_min' => $lat_min,
            'ns' => $ns,
            'avatar' => $avatar,
            'timezone' => 8,//app没有选择时区的地方，默认为8，需要前端进行提示
            'is_summer' => $is_summer
        ];

        $data['avatar'] = $data['avatar'] ?: $this->miniInfo['avatar'];
        if ($rid)
        {   // 更新档案
            (new Recode())->recodeEdit($rid, $data);
        } else
        {
            // 添加档案
            if ($this->user['has_self_recode'])
            {
                return show(422, '档案已存在，新增失败！', ['rid' => $this->user['rid']]);
            }
            $recodeInfo = (new Recode())->miniappRecodeAdd($data, $envType);
            $rid = $recodeInfo['id'];
        }

        return show(config('code.success'), '档案' . $resMsg . '成功', ['rid' => $rid]);
    }

    /**
     * 获取用户token信息
     * @param string $third_id
     * @return array
     */
    public function getUserTokenInfo($third_id = ''): array
    {
        // 生成token  和 token到期时间
        $token = IAuth::setAppLoginToken($third_id);
        $data = [
            'access_token' => $token,
            'expires_in' => strtotime('+' . config('app.login_time_out_day') . ' days'),
        ];
        return $data;
    }

    public function recodeSaveOrRemove()
    {
        $rid = input('rid/s', '');
        $type = input('type/d', 2);
        $envType = $this->headers['app-type'];

        if (!$rid || !in_array($type, [1, 2], true))
        {
            return show(config('code.error'), '请求异常');
        }

        $res = null;
        if ($type === 1)
        {
            // 从缓存获取档案信息
            $recodeInfo = CacheManage::getInstance()->setCacheConfig('miniapp.html_recode', $rid)->getCache();
            if ($recodeInfo)
            {
                $recodeInfo['isself'] = $this->user['rid'] ? 0 : 1;
                $recodeInfo['uid'] = (int)$this->user['id'];
                $result = (new Recode())->miniappRecodeAdd($recodeInfo, $envType);
                // 删除缓存
                CacheManage::getInstance()->setCacheConfig('miniapp.html_recode', $rid)->rmCache();
                $res = ['rid' => $result['id']];
            } else
            {
                return show(config('code.error'), '档案不存在');
            }
        } else
        {
            // 删除缓存
            CacheManage::getInstance()->setCacheConfig('miniapp.html_recode', $rid)->rmCache();
        }

        return show(config('code.success'), '请求成功', $res);
    }

    /**
     * 获取账户模拟 用户rid
     * @param int $uid 登陆用户uid
     * @return int
     * @throws ApiException
     */
    private function getSimulationRid(int $uid): int
    {
        // 账号模拟
        $simulation = config('app.simulation');

        if ($uid !== $simulation['from'])
        {
            $resUid = $uid;
        } else
        {
            $resUid = $simulation['to'] ?: $uid;
        }

        $userInfo = (new \app\common\model\User())->getUserinfo(['id' => $resUid]);
        if ($userInfo['forbidden'] == 1)
        {
            throw new ApiException('该账号禁止登录', 200, 0);
        }

        return (int)($userInfo['rid'] ?? 0);  // 是否有自己的档案
    }

    /**
     * 获取档案信息
     */
    public function getRecodeInfo()
    {
        $rid = input('rid/d', 0);
        if (!$rid)
        {
            return show(config('code.error'), '请求异常');
        }

        $recodeInfo = (new Recode())->getInfoById($rid, ['id', 'name', 'type', 'year', 'month', 'day', 'hour', 'minute', 'timezone', 'birth_country', 'birth_province', 'birth_city', 'live_country', 'live_province', 'live_city', 'comments', 'isself', 'long_deg', 'long_min', 'ew', 'lat_deg', 'lat_min', 'ns', 'avatar', 'is_summer', 'uid']);

        if (!$recodeInfo || $recodeInfo['uid'] != $this->user['id'])
        {
            return show(config('code.error'), '服务异常');
        }
        unset($recodeInfo['uid']);

        return show(config('code.success'), '请求成功', $recodeInfo);
    }

    /**
     * @param int $uid
     * @param string $miniApp 小程序类型：SWAN：百度小程序；WEAPP：\n微信小程序；QQ:qq小程序；ALIPAY：支付宝小程序；TT：字节跳动小程序；H5：h5；
     * @param int $appType 1星盘小程序 2合盘小程序
     * @return int
     */
    private function checkPhoneHasBindAcount(int $uid, string $miniApp, int $appType): int
    {
        try
        {
            $mid = (new MiniAppUser())->where([
                'uid' => $uid,
                'mini_app' => $miniApp,
                'app_type' => $appType
            ])->value('id');
        }
        catch (\Exception $e)
        {
            $mid = 0;
        }

        return $mid ?? 0;
    }
}
